package perfiladoinmdm;

import java.util.Random;
import perfiladoinmdm.services.DatabaseServiceImpl;
import perfiladoinmdm.services.FileServiceImpl;
import perfiladoinmdm.util.Util;


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author mrequejo
 */
public class PerfiladoMedidas {

    //String pathCoefREE = "D:\\Mis Documentos\\Miguel Díaz\\Perfilado_09.14\\coeficientesREE.txt";
    
    //String pathMedidaAgregada = "D:\\Mis Documentos\\Miguel Díaz\\Perfilado_09.14\\input_data\\MedidaAgregada.txt";
    String pathMedidaAgregada = "..\\input_data\\MedidaAgregada.txt";

    double[][] coefABG = {{1.3f, 0, 1.1f, 0.6f}, {1.2f, 1.5f, 1, 0.6f}, {1.6f, 2, 1.3f, 0.8f}};

    double[][] coeficientes = new double[8760][9];
    // Estructura de datos:
    // coeficientes: [mes día hora pesoA pesoB pesoC pesoD demanda díaGlobal] 

    double[][] medidaAgregada = new double[1][7];
    // medidaAgregada: [clienteID díaInicio mesInicio díaFinal mesFinal bloqueHorario medida] 
    double[] medidaHoraria = new double[8760];
    
    double[] ejeXPlot = new double[8760];

    // demandaSistema se genera aleatoriamente para hacer pruebas
    // las segunda columna de las demandas contien el día para agregar las horas
    // con la funcion agregarCoefs
    double[][] demandaSistema = new double[8760][2]; //[demandaSistema díaGlobal]
    double[][] demandaSistemaRef = new double[8760][2]; //[demandaSistemaRef díaGlobal]

    double[][] P0 = new double[8760][5]; //P0: [pesoA pesoB pesoC pesoD díaGlobal] 
    double[][] Pf = new double[8760][4];

    double[][] C0 = new double[365][5];  //C0: [pesoA pesoB pesoC pesoD mes] 
    double[][] C1 = new double[365][5];  //C0: [pesoA pesoB pesoC pesoD mes]
    double[][] Cf = new double[365][4];

    double[][] H0 = new double[8760][4];
    double[][] H1 = new double[8760][5]; //C0: [pesoA pesoB pesoC pesoD díaGlobal]
    double[][] Hf = new double[8760][4];

    double[][] M0 = new double[12][4];
    double[][] Mf = new double[12][4];

    int[][] diasMesAño = new int[12][2]; //diasMesAño: [mes días]
    
    private DatabaseServiceImpl databaseService = new DatabaseServiceImpl();
    private FileServiceImpl fileService = new FileServiceImpl();
    
    public PerfiladoMedidas() {
        //this.coeficientes = this.txt2Array(this.pathCoefREE, 8760, 9);
        this.coeficientes = this.databaseService.getCoeficientes();
        System.out.println("Pintando matriz coeficientes");        
        Util.plotMatrix(this.coeficientes);
        this.diasMesAño = calcularDiasMesAño(this.coeficientes);
//        System.out.println("Pintando matriz de días de los meses");
//        this.plotMatrix(this.castArray2Double(this.diasMesAño));

        this.medidaAgregada = Util.txt2Array(this.pathMedidaAgregada, 1, 7);
//        System.out.println("Pintando matriz de medidaAgregada");
//        this.plotMatrix(this.medidaAgregada);

        this.iniciarEjeXPLot();
//        System.out.println("Pintando ejeXplot");
//        this.plotArray(this.ejeXPlot);
        
        inicDemandas();
        
        this.Pf = calculoPesos(this.coeficientes);
        
//        //Para calcular el tiempo medio del algoritmo
//        long time_start = 0;
//        long time_end = 0;
//        long contadorGlobal = 0;
//        for (int i = 0; i < 1000; i++) {
//            double[][] matriz = new double[8760][9];
//            try {
//                Thread.sleep(100);
//            } catch (Exception e) {
//            // Mensaje en caso de que falle
//            }
//            time_start = System.currentTimeMillis();
//            matriz = calculoPesos(this.coeficientes);
//            time_end = System.currentTimeMillis();
//            System.out.println("tiempo del algoritmo: "+ (time_end - time_start));
//            contadorGlobal += time_end - time_start;
//        }
//        System.out.println("Tiempo medio del algoritmo: "+contadorGlobal/1000);

    }

    public static void main(String[] arguments) {
        PerfiladoMedidas pi = new PerfiladoMedidas();
//        System.out.println("Pintando matriz coeficientes alpha beta gamma");
//        pi.plotMatrix(pi.coefABG, 3);
        pi.medidaHoraria = pi.perfilado(pi.Pf, pi.medidaAgregada);
//        System.out.println("Pintando medida Horaria");
//        pi.plotArray(pi.medidaHoraria);
        Util.plotFuncionJFreeChart(pi.medidaHoraria);
        Util.plotFuncionJFreeChart(pi.perfilado(pi.coeficientes, pi.medidaAgregada));
    }

    public void iniciarEjeXPLot() {
        String cadena, cadena1, cadena2, cadena3;
        for (int i = 0; i < coeficientes.length; i++) {
            //cadena1 = String.valueOf(coeficientes[i][0]);
            cadena2 = Integer.toString((int) coeficientes[i][8]);        
            cadena3 = Integer.toString((int) coeficientes[i][2]);        
            //cadena = cadena1 + "." + cadena2 + "." +  cadena3;
            cadena = cadena2 + "." +  cadena3;
            //System.out.println("cadena:" + cadena);
            ejeXPlot[i] = Double.parseDouble(cadena);
        }
    }
    
    public double[] perfilado(double[][] perfilesFinales, double[][] medidaAgregada) {

        double[] resultado = new double[8760];

        int diaInicio = (int) medidaAgregada[0][1];
        int mesInicio = (int) medidaAgregada[0][2];

        int diaFinal = (int) medidaAgregada[0][3];
        int mesFinal = (int) medidaAgregada[0][4];

        int diaGlobalInicio = calcularDiaGlobal(mesInicio, diaInicio);
//        System.out.println("diaGlobal:" + diaGlobalInicio);
        int filaInicial = calcularFilaInicial(diaGlobalInicio);
//        System.out.println("filaInicial:" + filaInicial);
        int filaFinal = calcularFilaFinal(mesFinal, diaFinal);
//        System.out.println("filaFinal:" + filaFinal);
        double[] sum = sumSubMatrix(Pf, 0, 0, filaInicial, filaFinal);

//        System.out.println("Pintando sum");
//        Util.plotArray(sum);

        for (int i = filaInicial; i <= filaFinal; i++) {
            resultado[i] = Pf[i][0] * medidaAgregada[0][6] / sum[0];
        }
        return resultado;
    }

    public int calcularDiaGlobal(int mes, int dia) {
        int diaGlobal = 0;
        //int[][] diasMesAño = calcularDiasMesAño(coeficientes);
        for (int i = 0; i < 8760; i++) {
            if (coeficientes[i][0] == mes && coeficientes[i][1] == dia
                    && coeficientes[i][2] == 1) {
                diaGlobal = (int) coeficientes[i][8];
                break;
            }
        }
        return diaGlobal;
    }

    public int calcularFilaInicial(int diaGlobal) {
        int fila = 0;
        for (int i = 0; i < 8760; i++) {
            if (coeficientes[i][8] == diaGlobal) {
                fila = i;
                break;
            }

        }
        return fila;
    }

    public int calcularFilaFinal(int mesFinal, int diaFinal) {
        int fila = 0;
        for (int i = 0; i < 8760; i++) {
            if ((int) coeficientes[i][0] == mesFinal && (int) coeficientes[i][1]
                    == diaFinal && (int) coeficientes[i][2] != (int) coeficientes[i + 1][2]) {
                fila = i;
                break;
            }
        }
        return fila;
    }

    public int[][] calcularDiasMesAño(double[][] coeficientes) {
        int[][] resultado = new int[12][2];

        // Guardar en la primera columna el mes del cual vamos a contar los días
        for (int i = 0; i < 12; i++) {
            resultado[i][0] = i + 1;
            resultado[i][1] = 0;
        }

        for (int i = 0; i < coeficientes.length; i++) {
            for (int j = 0; j < 12; j++) {
                if ((int) coeficientes[i][0] == j + 1 && (int) coeficientes[i][2] == 1) {
                    resultado[j][1]++;
                }
            }
        }
        return resultado;
    }

    public double[][] calculoPesos(double[][] coeficientes) {
        
        double[][] Pf = new double[8760][4];
        //Cálculo de matriz P0
        for (int i = 0; i < this.P0.length; i++) {
            this.P0[i][0] = coeficientes[i][3];
            this.P0[i][1] = coeficientes[i][4];
            this.P0[i][2] = coeficientes[i][5];
            this.P0[i][3] = coeficientes[i][6];
            //Añadir el día global del año en la 5ª columna de P0:
            this.P0[i][4] = coeficientes[i][8];
        }
//        System.out.println("Pintando P inicial");
//        Util.plotMatrix(this.P0, 5);

        //Cálculo de matriz C0 con función agregarCoefs 
        this.C0 = agregarCoefs(P0, 4, 5);
        //Añadir el mes en la 5ª columna de C0:
        for (int i = 0; i < P0.length; i++) {
            C0[(int) P0[i][4] - 1][4] = coeficientes[i][0];
        }

//        System.out.println("Pintando C inicial");
//        Util.plotMatrix(this.C0, 5);

        //Cálculo de matriz H0
        for (int i = 0; i < coeficientes.length; i++) {
            for (int j = 0; j < H0[0].length; j++) {
                H0[i][j] = P0[i][j] / C0[(int) coeficientes[i][8] - 1][j];
            }
        }
//        System.out.println("Pintando H inicial");
//        Util.plotMatrix(this.H0, 5);

        //Cálculo de matriz M0 con la ayuda de funciones agregarCoefs y sumFilas 
        double[][] sumC0D = agregarCoefs(C0, 4, 4);
        double[] sumDsumMC0 = this.sumFilas(agregarCoefs(C0, 4, 4), 4);
        this.M0 = agregarCoefs(C0, 4, 4);
//        System.out.println("Pintando agregarCoefs(C0, 4);");
//        plotMatrix(this.M0, 12);
        for (int i = 0; i < 12; i++) {
            for (int j = 0; j < 4; j++) {
                this.M0[i][j] = sumC0D[i][j] / sumDsumMC0[j];
            }
        }
//        System.out.println("Pintando M inicial");
//        Util.plotMatrix(this.M0);

        double[] sumDH = agregarCoefs(demandaSistema);
        double[] sumDRH = agregarCoefs(demandaSistemaRef);

        //Cálculo de matriz H1
        for (int i = 0; i < H0.length; i++) {
            for (int j = 0; j < H0[0].length; j++) {
                this.H1[i][j] = this.H0[i][j] * (1 + this.coefABG[0][j]
                        * ((demandaSistema[i][0] / sumDH[(int) coeficientes[i][8] - 1])
                        / (demandaSistemaRef[i][0] / sumDRH[(int) coeficientes[i][8] - 1])
                        - 1));
            }
        }
        //Añadir día global del año en la 5ª columna de H1
        for (int i = 0; i < H0.length; i++) {
            H1[i][4] = coeficientes[i][8];
        }
//        System.out.println("Pintando H1 ");
//        Util.plotMatrix(this.H1, 25);

        //Cálculo de matriz Hf
        double[][] sumHH = agregarCoefs(H1, 4, 4);
        for (int i = 0; i < H1.length; i++) {
            for (int j = 0; j < H0[0].length; j++) {
                this.Hf[i][j] = this.H1[i][j] / (sumHH[(int) coeficientes[i][8] - 1][j]);
            }
        }
//        System.out.println("Pintando Hf ");
//        Util.plotMatrix(this.Hf, 25);

        //Cálculo de matriz C1        
        double sumaD = sumArray(sumDH);
        double sumaDR = sumArray(sumDRH);
        for (int i = 0; i < C0.length; i++) {
            for (int j = 0; j < 4; j++) {
                this.C1[i][j] = this.C0[i][j] * (1 + this.coefABG[1][j]
                        * ((sumDH[i] / sumaD) / (sumDRH[i] / sumaDR) - 1));
            }
        }
        //Añadir el mes en la 5ª columna de C1:
        for (int i = 0; i < P0.length; i++) {
            C1[(int) P0[i][4] - 1][4] = coeficientes[i][0];
        }
//        System.out.println("Pintando C1 ");
//        Util.plotMatrix(this.C1, 25);

        //Cálculo de matriz Cf
        double[][] sumCD = agregarCoefs(C1, 4, 4);
        for (int i = 0; i < C1.length; i++) {
            for (int j = 0; j < 4; j++) {
                this.Cf[i][j] = this.C1[i][j] / (sumCD[(int) C1[i][4] - 1][j]);
            }
        }
//        System.out.println("Pintando Cf ");
//        Util.plotMatrix(this.Cf, 25);

        //Cálculo de matriz Mf        
        for (int i = 0; i < 12; i++) {
            for (int j = 0; j < 4; j++) {
                this.Mf[i][j] = this.M0[i][j] * (1 + this.coefABG[2][j]
                        * ((sumaD / sumaDR) - 1));
            }
        }
//        System.out.println("Pintando Mf ");
//        Util.plotMatrix(this.Mf);

        //Cálculo de matriz Pf        
        for (int i = 0; i < Pf.length; i++) {
            for (int j = 0; j < 4; j++) {
                Pf[i][j] = Hf[i][j] * Cf[(int) coeficientes[i][8] - 1][j]
                        * Mf[(int) coeficientes[i][0] - 1][j];
            }
        }
//        System.out.println("Pintando Pf ");
//        Util.plotMatrix(Pf, 10);
        
        return Pf;
    }

    public double[] sumFilas(double[][] matrix, int numColumnas) {

        double[] resultado = new double[numColumnas];

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < numColumnas; j++) {
                resultado[j] += matrix[i][j];
            }
        }
        return resultado;
    }

    public double[] sumSubMatrix(double[][] matrix, int colInicial, int colFinal,
            int filaInicial, int filaFinal) {

        int numColumnas = 1 + colFinal - colInicial;
        double[] resultado = new double[numColumnas];

        for (int i = filaInicial; i <= filaFinal; i++) {
            for (int j = colInicial; j <= colFinal; j++) {
                //System.out.println("he entrado en el bucle j");
                resultado[j - colInicial] += matrix[i][j];
            }
        }
        return resultado;
    }

    public double sumArray(double[] vector) {
        double resultado = 0;
        for (int i = 0; i < vector.length; i++) {
            resultado += vector[i];
        }
        return resultado;
    }

    //k es el índice en el cual se indica el intervalo temporal inmediatamente 
    //superior al que se quiere agregar. Ejemplo: para agregar las horas habría
    //que indicar la columna de los días
    public double[][] agregarCoefs(double[][] matrix, int k, int numColumnasSalida) {

        int dim1 = 0;
        if (matrix.length == 8760) {
            dim1 = 365;
        } else if (matrix.length == 365) {
            dim1 = 12;
        }

        double[][] resultado = new double[dim1][numColumnasSalida];
        //Error grave: double[][] resultado = matrix;

        double[] suma = {0, 0, 0, 0};
        int j = 0;

        for (int i = 0; i < matrix.length - 1; i++) {
            if (matrix[i][k] == matrix[i + 1][k]) {
                suma[0] += matrix[i][0];
                suma[1] += matrix[i][1];
                suma[2] += matrix[i][2];
                suma[3] += matrix[i][3];
                //Si llegamos al penúltimo elemento agregamos el último elemento:
                if (i == matrix.length - 2) {
                    suma[0] += matrix[i + 1][0];
                    suma[1] += matrix[i + 1][1];
                    suma[2] += matrix[i + 1][2];
                    suma[3] += matrix[i + 1][3];

                    resultado[j][0] = suma[0];
                    resultado[j][1] = suma[1];
                    resultado[j][2] = suma[2];
                    resultado[j][3] = suma[3];

                    suma[0] = 0;
                    suma[1] = 0;
                    suma[2] = 0;
                    suma[3] = 0;

                    j++;
                }
            } else {
                suma[0] += matrix[i][0];
                suma[1] += matrix[i][1];
                suma[2] += matrix[i][2];
                suma[3] += matrix[i][3];

                resultado[j][0] = suma[0];
                resultado[j][1] = suma[1];
                resultado[j][2] = suma[2];
                resultado[j][3] = suma[3];

                suma[0] = 0;
                suma[1] = 0;
                suma[2] = 0;
                suma[3] = 0;

                j++;
            }
        }
        //System.out.println("tras aplicar agregarCoefs(double[][] matrix, int k) el array tiene " + (j) + " filas");
        return resultado;
    }

    public double[] agregarCoefs(double[][] matrix) {
        int max = 0;
        for (int i = 0; i < matrix.length; i++) {
            if (matrix[i][1] > max) {
                max = (int) matrix[i][1];
            }
        }
        //System.out.println("Máximo "+max);
        double[] resultado = new double[max];
        double suma = 0;
        int j = 0;

        for (int i = 0; i < matrix.length - 1; i++) {
            if ((int) matrix[i][1] == (int) matrix[i + 1][1]) {
                suma += matrix[i][0];
                //Si llegamos al penúltimo elemento agregamos el último elemento:
                if (i == matrix.length - 2) {
                    suma += matrix[i + 1][0];

                    resultado[j] = suma;

                    suma = 0;

                    j++;
                }
            } else {
                suma += matrix[i][0];

                resultado[j] = suma;

                suma = 0;

                j++;
            }
        }
        //System.out.println("tras aplicar agregarCoefs el array tiene " + (j) + " filas");
        return resultado;
    }

    public void generarDemandaDePrueba() {
        for (int i = 0; i < coeficientes.length; i++) {
            Random num = new Random();
            demandaSistema[i][0] = coeficientes[i][7];// * (1 + 0.05 * num.nextGaussian());
            //Añadir día global del año
            demandaSistema[i][1] = coeficientes[i][8];
        }
//        System.out.println("Pintando demanda sistema");
//        plotMatrix(demandaSistema);
    }

    public void inicDemandas() {

        generarDemandaDePrueba();
        for (int i = 0; i < coeficientes.length; i++) {
            demandaSistemaRef[i][0] = coeficientes[i][7];
            //Añadir día global del año
            demandaSistemaRef[i][1] = coeficientes[i][8];
        }
    }


}
